/********************************************************************/
/*********************************** File header.h ******************/
/********************************************************************/
/***** Define simulation *****/
#define MPL 4

// the number of interactive processes
#define N 90

//the cpu service time of interactive processes
#define TCPU 0.08
#define TQuantum 0.1
//#define TInterRequest 0.04
#define TIO  0.016      

#define TDiskService 0.005
#define TThink 5
#define TTotal 10000

//change: context switch time
#define ContextSwitch 0.0005

#define MemoryQueue 0
#define CPUQueue 1
#define DiskQueue 2
#define RequestMemory 0
#define RequestCPU 1
#define ReleaseCPU 2
#define RequestDisk 3
#define ReleaseDisk 4
#define num_CPU 4
#define num_disk 1
#define EMPTY -1
#define LowPriority 0
#define HighPriority 1

//the number of parallel processes
#define ppnum 6

//Miles: the cpu service time of parallel processes (0.4s or 2s)
#define TBS 2

//flag of interactive processes
#define INTER 1

//flag of parallel processes
#define PARA 0

/* change CyclesPerInstru when hit rate changes **/
#define CyclesPerInstru  3
#define TimePerInstru 0.000000003
#define mem_size 8192
#define OS_size 512
#define proc_mem (mem_size - OS_size)/MPL
#define fm pow(2.0, -(proc_mem/135.0 + 17.0))
#define TPAGE 1/fm*TimePerInstru

#include <math.h>
#include <stdio.h>
#include <iostream>
#include <cstdlib>
#include <vector>
using namespace std;

/**** Job list       ****/
//t_io: the io interrupt time of the process
//t_page: the paging interval of the process
//disk_id: the disk that service the process (need to designate when creating the process)
//task_type: the type of the process, interactive or parallel
//change: the number of tasks should be N + ppnum
struct Task {
	double tcpu, tquantum, t_io, t_page, start;
	int cpu_id, disk_id;
	int task_type;
} task[N+ppnum];

/**** Event list           ****/
//q: the length of the event list
struct Events {
	int head, tail, q;
	double time[N+ppnum];
	int task[N+ppnum], event[N+ppnum];
} elist;

/**** Queues: 0 - memory queue, 1 - CPU queue, 2 - Disk queue*/
//q: the current length of the queue
//n: the total processes that have entered into the queue
//ws: accumulated waiting time
//tch: last change time
//ts: length and time accumulator
struct Queue {
	int head, tail, q, n, task[N+ppnum];
	double ws, tch, ts, tentry[N+ppnum];
} queue[3];

/*** CPU # of CPU is 4*/
struct CPU {
	int busy;
	double tch, tser;
} cpu[num_CPU];

/*** # of disks is 1 ***/
struct Disk {
	int busy;
	double tch, tser;
} disk[num_disk];

/* inmemory, actual number of tasks in memory ****/
int inmemory = 0;

// the number of finished processes, including interative and parallel processes
int finished_tasks = 0;

//total response time of interactive and parallel processes
double sum_response_time = 0.0;

//Flag that show whether the parallel processes in the BS or not
//false means that still in execution, true means that it has already executed and in BS now
bool barrier_status[ppnum];



/****  Event procedures      ****/
void Process_RequestMemory(int, double);
void Process_RequestCPU(int, double);
void Process_ReleaseCPU(int, double);
void Process_RequestDisk(int, double);
void Process_ReleaseDisk(int, double);

/****  Auxiliary functions   ****/
double random_exponential(double);
void place_in_queue(int, double, int);
void create_event(int, int, double, int);
void init();
void stats();

int remove_from_queue(int, double);
int random_server_num(int, int);
void createParallelProcesses(double time);
bool checkSynchronizationStatus();
